package pipeline;

import baseObjects.Language;
import baseObjects.Symbol;
import corpus.Observable;
import corpus.Observer;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Properties;
import java.util.Scanner;
import java.util.Vector;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

public class KeyboardProducerB extends Thread implements Observable {

    protected SynchronizedBuffer keyboards = null;
    protected Language lang = null;
    protected Properties keyPriority = null;
    protected BigInteger numOfPermutations;
    protected Vector<Observer> observers = null;
    protected boolean generic;
    //saved data
    protected int Priority1Amount = 0;
    protected int Priority2Amount = 0;
    protected int Priority3Amount = 0;
    protected int Priority4Amount = 0;
    protected int Priority5Amount = 0;
    protected int Priority6Amount = 0;
    static protected int NumOfKeysInLayout = 47;
    /**keycode->priority**/
    protected HashMap<Integer, Integer> priorityMap = null;
    /**symbol->priority**/
    protected HashMap<Symbol, Integer> symbolPrioroty = null;

    protected Logger success = Logger.getLogger(this.getClass().getName()+".success");
    protected Logger failed = Logger.getLogger(this.getClass().getName()+".failed");

    public KeyboardProducerB(SynchronizedBuffer buff, Language lang, Properties keyPriority) {
        //System.out.println("KeyboardProducer ctor");
                FileHandler fh,fh2;
        try{
            fh = new FileHandler(this.getClass().getName()+".success",10*1024*1024,1,  false);
            fh2 = new FileHandler(this.getClass().getName()+".failed", 10*1024*1024,1, false);
            success.addHandler(fh);
            failed.addHandler(fh2);
            success.setLevel(Level.ALL);
            failed.setLevel(Level.ALL);
            fh.setFormatter(new SimpleFormatter());
            fh2.setFormatter(new SimpleFormatter());
        }catch(Throwable t){
            System.err.println(t.getMessage());
        }
        keyboards = buff;
        this.lang = lang;
        this.keyPriority = keyPriority;
        this.numOfPermutations = calcPermutations();
        observers = new Vector<Observer>();
        //System.out.printf("perms: %,d\n", numOfPermutations);
        generic = false;
      
        priorityMap = new HashMap<Integer, Integer>();
        
        symbolPrioroty = new HashMap<Symbol, Integer>();

        Vector<Symbol> pri = lang.getSymbolsSortet();

        Priority1Amount = keyPriority.getProperty("1").split(",").length;
        //System.out.println("priority1 amount = " + Priority1Amount);

        Priority2Amount = keyPriority.getProperty("2").split(",").length;
        //System.out.println("priority2 amount = " + Priority2Amount);

        Priority3Amount = keyPriority.getProperty("3").split(",").length;
        //System.out.println("priority3 amount = " + Priority3Amount);

        Priority4Amount = keyPriority.getProperty("4").split(",").length;
        //System.out.println("priority4 amount = " + Priority4Amount);

        Priority5Amount = keyPriority.getProperty("5").split(",").length;
        //System.out.println("priority5 amount = " + Priority5Amount);

        Priority6Amount = keyPriority.getProperty("6").split(",").length;
        //System.out.println("priority6 amount = " + Priority6Amount);

        int place = 0;
        while (place < pri.size()) {
            Object[] arr = keyPriority.getProperty("1").split(",");
            Integer p = new Integer(1);
            for (int i = 0; i < Priority1Amount && place < pri.size(); i++) {
                if (!Character.isDigit(pri.get(place).getRegular())) {
                    priorityMap.put(Integer.parseInt(((String) arr[i]).trim()), p);
                    symbolPrioroty.put(pri.get(place), p);
//                    System.out.println("symbol "+pri.get(place)+" priority is "+p);
//                    System.out.println("verify: "+symbolPrioroty.get(pri.get(place)));
                } else {
                    i--;
                }
                place++;

            }
//            new Scanner(System.in).next();
            arr = keyPriority.getProperty("2").split(",");
            p = new Integer(2);
            for (int i = 0; i < Priority2Amount && place < pri.size(); i++) {
                if (!Character.isDigit(pri.get(place).getRegular())) {
                    priorityMap.put(Integer.parseInt(((String) arr[i]).trim()), p);
                    symbolPrioroty.put(pri.get(place), p);
                } else {
                    i--;
                }
                place++;
            }

            arr = keyPriority.getProperty("3").split(",");
            p = new Integer(3);
            for (int i = 0; i < Priority3Amount && place < pri.size(); i++) {
                if (!Character.isDigit(pri.get(place).getRegular())) {
                    priorityMap.put(Integer.parseInt(((String) arr[i]).trim()), p);
                    symbolPrioroty.put(pri.get(place), p);
                } else {
                    i--;
                }
                place++;
            }

            arr = keyPriority.getProperty("4").split(",");
            p = new Integer(4);
            for (int i = 0; i < Priority4Amount && place < pri.size(); i++) {
                if (!Character.isDigit(pri.get(place).getRegular())) {
                    priorityMap.put(Integer.parseInt(((String) arr[i]).trim()), p);
                    symbolPrioroty.put(pri.get(place), p);
                } else {
                    i--;
                }
                place++;
            }

            arr = keyPriority.getProperty("5").split(",");
            p = new Integer(5);
            for (int i = 0; i < Priority5Amount && place < pri.size(); i++) {
                if (!Character.isDigit(pri.get(place).getRegular())) {
                    priorityMap.put(Integer.parseInt(((String) arr[i]).trim()), p);
                    symbolPrioroty.put(pri.get(place), p);
                } else {
                    i--;
                }
                place++;
            }

            arr = keyPriority.getProperty("6").split(",");
            p = new Integer(6);
            for (int i = 0; i < Priority6Amount && place < pri.size(); i++) {
                if (!Character.isDigit(pri.get(place).getRegular())) {
                    priorityMap.put(Integer.parseInt(((String) arr[i]).trim()), p);
                    symbolPrioroty.put(pri.get(place), p);
                } else {
                    i--;
                }
                place++;
            }
        }
//        for (Symbol s : symbolPrioroty.keySet()){
//            System.out.println(s+" = "+symbolPrioroty.get(s));
//        }
//        new Scanner(System.in).next();
    }

    public boolean isGeneric() {
        return generic;
    }

    public void setGeneric(boolean generic) {
        this.generic = generic;
    }

    protected void produce() {
        //4 layouts. 0: reg, 1: shift, 2: ctrl, 3: alt
        Symbol[][] layouts = new Symbol[4][];

        Vector<Symbol> sorted = lang.getSymbolsSortet();

        if (generic) {
            for (int i = 0; i
                    < sorted.size(); i++) {
                if (Character.isDigit(sorted.get(i).getRegular())) {
                    sorted.remove(sorted.get(i));
                    i--;
                }
            }
            layouts[0] = new Symbol[NumOfKeysInLayout - 10];
        } else {
            layouts[0] = new Symbol[NumOfKeysInLayout];
        }

        for (int i = 1; i
                < 4; i++) {
            layouts[i] = new Symbol[NumOfKeysInLayout];
        }

        int numOfLayouts = sorted.size() / NumOfKeysInLayout + (sorted.size() % NumOfKeysInLayout != 0 ? 1 : 0);


        int totalSymbolsColected = 0;
        int layout = 0;
        for (; layout < numOfLayouts; layout++) {

            int i = 0;
            for (; i
                    < layouts[layout].length && totalSymbolsColected < sorted.size(); i++) {
                layouts[layout][i] = sorted.get(totalSymbolsColected++);
            }

            if (i < NumOfKeysInLayout) {
                Symbol[] tmp = layouts[layout];
                layouts[layout] = new Symbol[i];
                for (int j = 0; j
                        < i; j++) {
                    layouts[layout][j] = tmp[j];
                }

            }
        }
//remove unessential layouts:

        for (int i = layout; i
                < 4; i++) {
            layouts[i] = new Symbol[0];
        }

// generator for each Layout
        Permuter<Symbol> genReg = new Permuter<Symbol>(layouts[0]);
        Permuter<Symbol> genShift = new Permuter<Symbol>(layouts[1]);
        Permuter<Symbol> genCtrl = new Permuter<Symbol>(layouts[2]);
        Permuter<Symbol> genAlt = new Permuter<Symbol>(layouts[3]);

        BigInteger total = genReg.getTotal().add(genShift.getTotal().add(genCtrl.getTotal().add(genAlt.getTotal())));

        BigInteger i = new BigInteger("1");

        for (Symbol[] alt : genAlt) {
            for (Symbol[] ctrl : genCtrl) {
                for (Symbol[] shift : genShift) {
                    for (Symbol[] reg : genReg) {
                        KeyboardCandidate candi = generate(reg, shift, ctrl, alt);
                        if (filter(candi,i)){
                            success.log(Level.ALL,i+":\n"+candi.toString());
                            keyboards.add(candi);
                        }
                        i = i.add(BigInteger.ONE);
                    }
                }
            }
        }
    }
    

    private KeyboardCandidate generate(Symbol[] reg, Symbol[] shift, Symbol[] ctrl, Symbol[] alt) {
        KeyboardKey[] keymap = new KeyboardKey[47];
//        keymap[0] = new KeyboardKey[13];
//        keymap[1] = new KeyboardKey[13];
//        keymap[2] = new KeyboardKey[11];
//        keymap[3] = new KeyboardKey[10];

        if (generic) {
            keymap[1] = new KeyboardKey(lang.getNumber(1));
            keymap[2] = new KeyboardKey(lang.getNumber(2));
            keymap[3] = new KeyboardKey(lang.getNumber(3));
            keymap[4] = new KeyboardKey(lang.getNumber(4));
            keymap[5] = new KeyboardKey(lang.getNumber(5));
            keymap[6] = new KeyboardKey(lang.getNumber(6));
            keymap[7] = new KeyboardKey(lang.getNumber(7));
            keymap[8] = new KeyboardKey(lang.getNumber(8));
            keymap[9] = new KeyboardKey(lang.getNumber(9));
            keymap[10] = new KeyboardKey(lang.getNumber(0));
            //    updateAll("GENERIC : numeric keys are in place");
        }//if generic

//put reg layer:
        int regPlace = 0;
        int keyPlace = 0;
        while (regPlace < reg.length) {
            if (isNumericPlace(keyPlace)) {
                keyPlace++;
            } else {
//                //System.out.println("set reg @ "+keyPlace+" to "+reg[regPlace]);
                keymap[keyPlace++] = new KeyboardKey(reg[regPlace++]);
            }
        }
        if (keyPlace == keymap.length) {
            keyPlace = 0;
        }
//        //System.out.println("SHIFT");
//put shift layer

        int shiftPlace = 0;
        while (shiftPlace < shift.length) {
            if (keymap[keyPlace].hasShift()) {
                keyPlace++;
                if (keyPlace == 0) {
                    //System.out.println("ERROR!");
                }

            } else {
                keymap[keyPlace++].setShift(shift[shiftPlace++].getRegular());
            }
        }

        if (keyPlace == keymap.length) {
            keyPlace = 0;
        }
//        //System.out.println("ALT");

        int altPlace = 0;
        while (altPlace < alt.length) {
            if (keymap[keyPlace].hasAlt()) {
                keyPlace++;
                if (keyPlace == 0) {
                    //System.out.println("ERROR!");
                }

            } else {
                keymap[keyPlace++].setAlt(alt[altPlace++].getRegular());
            }

        }
        if (keyPlace == keymap.length) {
            keyPlace = 0;
        }
//        //System.out.println("CTRL");

        int ctrlPlace = 0;
        while (ctrlPlace < ctrl.length) {
            if (keymap[keyPlace].hasCtrl()) {
                keyPlace++;
                if (keyPlace == 0) {
                    //System.out.println("ERROR!");
                }

            } else {
                keymap[keyPlace++].setCtrl(ctrl[ctrlPlace++].getRegular());
            }

        }


        //System.out.println("\n");
//        //print layout:
//        for (int i = 0; i < 13; i++) {
//            //System.out.print(keymap[i] + "|");
//        }
//
//        //System.out.println("");
//        for (int i = 0; i < 13; i++) {
//            //System.out.print(keymap[13 + i] + "|");
//        }
//
//        //System.out.println("");
//        for (int i = 0; i < 11; i++) {
//            //System.out.print(keymap[26 + i] + "|");
//        }
//        //System.out.println("");
//        for (int i = 0; i < 10; i++) {
//            //System.out.print(keymap[37 + i] + "|");
//        }
//        //System.out.println("");


        //keymap is ready return the keyboardMAp
//        //System.out.println("stop");
//        new Scanner(System.in).next();

        return new KeyboardCandidate(keymap);
    }

    private boolean isNumericPlace(int place) {
        return place >= 1 && place <= 10;
    }

    public void run() {
        //System.out.println("producing");
        this.produce();
        //System.out.println("done producing");
    }

    private long fact(int n) {
        if (n == 0) {
            return 1;
        }

        return n * fact(n - 1);
    }

    private BigInteger calcPermutations() {
//       int ans =0;
        BigInteger firstP = BigInteger.valueOf(fact(keyPriority.getProperty("1").split(",").length));
        //System.out.println("1st : " + firstP);
        BigInteger secondP = BigInteger.valueOf(fact(keyPriority.getProperty("2").split(",").length));
        //System.out.println("2nd : " + secondP);
        BigInteger thirdP = BigInteger.valueOf(fact(keyPriority.getProperty("3").split(",").length));
        //System.out.println("3rd : " + thirdP);
        BigInteger fourthP = BigInteger.valueOf(fact(keyPriority.getProperty("4").split(",").length));
        //System.out.println("4th : " + fourthP);
        BigInteger fifthP = BigInteger.valueOf(fact(keyPriority.getProperty("5").split(",").length));
        //System.out.println("5th : " + fifthP);
        BigInteger sixthP = BigInteger.valueOf(fact(keyPriority.getProperty("6").split(",").length));
        //System.out.println("6th : " + sixthP);
        return firstP.multiply(secondP.multiply(thirdP.multiply(fourthP.multiply(fifthP.multiply(sixthP)))));
    }

    public void addObserver(Observer o) {
        observers.add(o);
    }

    public void removeObserver(Observer o) {
        observers.removeElement(o);
    }

    public void updateAll(String s) {
        for (Observer observer : observers) {
            observer.recive(this, this.getClass().getName() + " : \n\t" + s);
        }
    }

private boolean filter_candidats(KeyboardCandidate candi) {

        boolean ans = false;
        Symbol[][] keymap = candi.getKeys();
        //print layout:
        int folowJ;
        // //System.out.println("filter!");
        if (keymap == null) {
            // System.err.println("STOP!!!!");
            //new Scanner(System.in).next();
            return ans;
        }
        //System.out.println("filter!\n");
        for (int i = 0; i < keymap.length; i++) {
            for (int j = 0; j < keymap[i].length; j++) {
                //folowJ = j - 1;                
                if (j > 0) {
                    Symbol tempSymbol = keymap[i][j];
                    // //System.out.print(tempSymbol.getRegular());
                    Symbol folowSymb = keymap[i][j - 1];

                    if ((tempSymbol.getRegular().equals('(') && folowSymb.getRegular().equals(')'))
                            || (tempSymbol.getRegular().equals(')') && folowSymb.getRegular().equals('('))
                            || (tempSymbol.getShift().equals('(') && folowSymb.getShift().equals(')'))
                            || (tempSymbol.getShift().equals(')') && folowSymb.getShift().equals('('))) {
                        //System.out.println("\n\ngood candidate () )(\n\n");
                        ans = true;
                    }
                }
            }
        }
        if (!ans) {
            System.err.println("STOP!!!!");
            new Scanner(System.in).next();
        }
        return ans;
    }


    private boolean filter(KeyboardCandidate candi, BigInteger num) {
        boolean ans = true;
        Symbol[][] keys = candi.getKeys();
//        System.out.println("looking @");
//        System.out.println(candi);
        for (int i = 0; i < keys.length; i++) {
            for (int j = 0; j < keys[i].length; j++) {
                Integer keycodeP=priorityMap.get(i*100+j+1);
                Symbol s = keys[i][j];
                Integer symbolP = symbolPrioroty.get(s);
                symbolPrioroty.keySet();
//                if (!priorityMap.get(new Integer(i*100+j+1)).equals(symbolPrioroty.get(keys[i][j]))){
                if(keycodeP!= null && symbolP!=null && !keycodeP.equals(symbolP)){
//                    System.out.println("candi failed because in keycode "+(i*100+j+1)+" ["+i+","+j+"] there was "+keys[i][j]+" and it's priority is :"+symbolPrioroty.get(keys[i][j]));
//                    new Scanner(System.in).next();
                    failed.log(Level.ALL,"candidate "+num+":\n"+candi.toString()+"\n failed becuse of in keycode "+(i*100+j+1)+" ["+i+","+j+"] there was "+keys[i][j]+" and it's priority is :"+symbolPrioroty.get(keys[i][j])+" instead of "+keycodeP);
                    return false;
                }
            }
        }
        System.out.println("candi passed!");
        System.out.println(candi);
        new Scanner(System.in).next();
        return ans;


    }
}






